package indigo.locale 
{
	import indigo.locale.errors.LocaleError;
	import indigo.locale.events.LocaleEvent;
	import indigo.utils.DisposableEventDispatcher;
	import indigo.utils.IDisposable;

	import flash.events.Event;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.Dictionary;

	/**
	 * Dispatches when the countryCode is changed by the <code>Locale</code> class
	 * @eventType indigo.locale.events.LocaleEvent
	 */
	[Event(name="countryCodeChange", type="indigo.locale.events.LocaleEvent")]	
	/**
	 * The <code>Locale</code> class allows you to control multilanguage text at runtime. The class imports and uses XML files exported by
	 * Microsoft Excel to control its data. 
	 * 
	 * <p>Once the XML file is loaded, <code>Data</code> can be accessed from anywhere in your application using the <code>getData</code> function.
	 * This will return an <code>Data</code> object which contains text and text styles.</p>
	 *   
	 * <p>You can select a supported language at runtime by setting the <code>countryCode</code>. By default the <code>Locale</code> select the first
	 * language found in the loaded XML. The <code>Locale</code> class dispatches a <code>LocaleEvent</code> notifying its listeners to the change. It is 
	 * possible to bind data to <code>TextFields</code> in your applicaton by using the <code>attachData</code> function. TextFields are automatically 
	 * updated when the selected <code>countryCode</code> is updated. 
	 * 
	 * <p>The <code>Locale</code> class provide functionality to copy text styles from Excel to your application. Although this is not recommended.</p>  
	 *
	 * @example
	 * <code>
	 * <listing version="3.0" >
	 * <pre>
	 * package  
	 * {
	 * 		import indigo.locale.Locale;
	 * 		import indigo.locale.getData;
	 * 		import indigo.service.events.ServiceEvent;
	 * 		import indigo.service.sendAndLoadXML;
	 * 
	 * 		import flash.display.Sprite;
	 * 		public class LocaleTest extends Sprite 
	 * 		{
	 * 			private var _output:TextField;
	 * 			public function Temp()
	 *	 		{
	 *	 			this._output = new TextField();
	 *	 			this.addChild( this._output );
	 *	 			
	 * 				sendAndLoadXML( "data.xml", this.onData );
	 * 			}
	 *	 		private function onData( event:ServiceEvent ):void
	 * 			{
	 * 				if ( event.type == ServiceEvent.RESULT )
	 * 				{
	 * 					// Set the loaded data
	 * 					Locale.getInstance().data = event.data;
	 * 					
	 * 					// Attach data to the output textfield
	 * 					attachData ( "title", this._output ); 
	 * 					
	 * 					// Change language to Dutch and display data
	 * 					setLanguage ( "nl-nl" );
	 * 					trace( getData( "title" ) );
	 * 					
	 * 					// Change language to English and display data
	 * 					setLanguage ( "en-en" );	 * 					trace( getData( "title" ) );
	 * 				}
	 * 			}
	 * 		}
	 * }
	 * </code>
	 * </listing>
	 * </pre>
	 *  
	 * @playerversion Flash 9
	 * @langversion ActionScript 3.0
	 */
	final public class Locale extends DisposableEventDispatcher implements IDisposable 
	{
		/**
		 * Namespaces used by Microsoft Excel
		 */
		namespace office = "urn:schemas-microsoft-com:office:office";
		use namespace office;
		
		namespace excel = "urn:schemas-microsoft-com:office:excel";
		use namespace excel;
		
		namespace spreadsheet = "urn:schemas-microsoft-com:office:spreadsheet";
		use namespace spreadsheet;

		/**
		 * Reference to the Locale class
		 */
		private static var INSTANCE:Locale = null;
		/**
		 * String that indicates the countryCode
		 */
		private var _countryCode:String;
		/**
		 * XML that holds locale data. The XML must be exported from Microsoft Excel.
		 */
		private var _data:XML;
		/**
		 * Dictionary that holds all target <code>TextFields</code> binded to data 
		 */
		private var _targetList:Dictionary = new Dictionary( true );
		/**
		 * Boolean that indicates that all target <code>TextFields</code> are automatically updated when the <code>countryCode</code> is changed
		 */
		public var autoUpdate:Boolean = true;

		
		/**
		 * Constructor
		 * 
		 */
		public function Locale( singleton:SingletonEnforcer )
		{
			log( "Instantiating" );
		}

		
		/**
		 * XML that holds locale data. The XML must be exported from Microsoft Excel.
		 * 
		 */		
		public function set data( value:XML ):void
		{
			for each ( var target:TextField in this._targetList )
				target.text = "";
						
			// Create a new ExcelFile
			this._data = value;
			
			// Use the first sheet when the default country code isn't supported 
			if ( this._countryCode == null || !this.languageIsSupported( this._countryCode ) )
				this.countryCode = this.supportedLanguages[ 0 ];
			else
				this.countryCode = this._countryCode;
		}

		
		/**
		 * Returns a <code>Boolean</code> that indicates the <code>Data</code> exists
		 * @param <code>String</code> that indicates the <code>Data</code> key
		 * @return <code>Boolean</code> that indicates the <code>Data</code> exists
		 * 
		 */
		public function hasData( key:String ):Boolean
		{
			if ( !this.isLoaded )
				throw new LocaleError( LocaleError.DATA_NOT_LOADED );
			
			return this.languageSheet..Row.( Cell[ 0 ]..Data.text( ) == key ) != null;
		}

		
		/**
		 * Returns the <code>Data</code> belonging to a data key
		 * @param <code>String</code> that indicates the <code>Data</code> key
		 * @return <code>String</code> that represents the <code>Data</code>
		 * 
		 */
		public function getData( key:String ):Data
		{
			// Check if Data is loaded
			if ( !this.isLoaded )
				throw new LocaleError( LocaleError.DATA_NOT_LOADED );

			// And if the key exists
			if ( !this.hasData( key ) )
				throw new LocaleError( LocaleError.DATA_NOT_FOUND );

			// Get the correspoding row
			var myRow:XMLList = this.languageSheet..Row.( Cell[ 0 ]..Data.text( ) == key );
			var myStyleID:String = String( myRow.Cell[ 1 ].@StyleID );	
			if ( myStyleID.length == 0 || myStyleID == null )
				myStyleID = "Default";
			
			// Get the data and textStyle
			var myText:String = myRow.Cell[ 1 ].Data.text( ); 
			var myTextFormat:XMLList = this._data..Style.( @ID == myStyleID ).Font;
			var myData:Data = new Data( myText, myTextFormat );
			
			return myData;
		}

		
		/**
		 * Attaches <code>Data</code> to a </code>TextField</code>. When the <code>countryCode</code> of the <code>Locale</code> 
		 * is updated all attached TextField will automatically update themself. By setting the <code>useTextFormat</code> argument 
		 * to true all <code>Textfields</code> will copy the text style exported from Excel.
		 * 
		 * @param <code>TextField</code> to bind
		 * @param <code>String</code> that indicates the <code>Data</code> key
		 * @param <code>Boolean</code> that indicates that <code>Data</code> will be binded as HTML
		 * @param <code>Boolean</code> that indicates that text style options exported from Excel will be copied
		 * 
		 */
		public function attachData( key:String, target:TextField, isHtmlText:Boolean = false, useTextFormat:Boolean = false ):void
		{
			// Store the target textfield
			this._targetList[ target ] = { key: key, target: target, isHtmlText: isHtmlText, useTextFormat: useTextFormat };
			
			// If the data exists
			if ( this.isLoaded && this.hasData( key ) )
			{
				var myData:Data = this.getData( key );
				
				// Set the Data
				if ( isHtmlText )
					target.htmlText = myData.text;
				else
					target.text = myData.text;
				
				target.autoSize = TextFieldAutoSize.LEFT;
				
				// Use auto text formatting				
				if ( useTextFormat )
				{
					var myFormat:TextFormat = target.getTextFormat( );
					myFormat.font = myData.fontName;
					myFormat.size = myData.size;
					myFormat.color = myData.color;
					
					myFormat.bold = myData.bold;
					myFormat.italic = myData.italic;
					myFormat.underline = myData.underline;
					
					target.setTextFormat( myFormat );
				}
			}
		}

		
		/**
		 * XMLList that indicates the language sheet belonging to the selected <code>countryCode</code>
		 * 
		 */
		private function get languageSheet():XMLList
		{
			if ( !this.isLoaded )
				throw new LocaleError( LocaleError.DATA_NOT_LOADED );
				
			return this._data..Worksheet.( @Name == this._countryCode );
		}	

		
		/**
		 * Array that holds all supported languages by the <code>Locale</code>
		 * 
		 */
		public function get supportedLanguages():Array
		{
			if ( !this.isLoaded )
				throw new LocaleError( LocaleError.DATA_NOT_LOADED );

			return this._data..Worksheet..@Name.toXMLString( ).split( "\n" );
		}

		
		/**
		 * String that indicates the countryCode
		 * 
		 */
		public function set countryCode( value:String ):void
		{
			// If the countryCode is updated
			if ( this._countryCode != value )
			{
				// Set the country code and active sheet	
				this._countryCode = value;
				if ( this.languageIsSupported( value ) )
				{				
					log( value );
					
					// Update all attached TextFields
					if ( this.autoUpdate )
					{
						for each ( var target:Object in this._targetList )
						{
							this.attachData( target.key, target.target, target.isHtmltext, target.useTextFormat );
						}
					}
					
					// Dispatch event
					if ( this.hasEventListener( LocaleEvent.COUNTRY_CODE_CHANGE ) )
					{				
						var myEvent:LocaleEvent = new LocaleEvent( LocaleEvent.COUNTRY_CODE_CHANGE );
						this.dispatchEvent( myEvent );
					}
				}
				
				// Throw error
				else if ( this.isLoaded )
					throw new LocaleError( LocaleError.LANGUAGE_NOT_SUPPORTED );
			}
		}

		
		/**
		 * String that indicates the countryCode
		 * 
		 */
		public function get countryCode():String
		{
			return this._countryCode;
		}

		
		/**
		 * Boolean that indicates a language is supported by the <code>Locale</code> class
		 * 
		 */
		public function languageIsSupported( countryCode:String ):Boolean
		{
			return this.isLoaded && this.supportedLanguages.indexOf( String( countryCode ) ) > -1;
		}

		
		/**
		 * Boolean that indicates that the <code>Locale</code> class holds data
		 *  
		 */
		public function get isLoaded():Boolean
		{
			return this._data != null;		
		}

		
		/**
		 * @inheritDoc
		 * 
		 */
		override public function dispose( event:Event = null ):void
		{
			for each ( var target:TextField in this._targetList )
				target.text = "";

			super.dispose( event );
				
			this._data = null;
			this._countryCode = null;
			this._targetList = null;
		}

		
		/**
		 * Returns the Singleton reference of the <code>Locale</code> class
		 * 
		 */
		public static function getInstance():Locale
		{
			if ( INSTANCE == null )
				INSTANCE = new Locale( new SingletonEnforcer );
			
			return INSTANCE;
		}		
	}
}


internal class SingletonEnforcer  
{
}
